//
// RLE.cpp
// libprotea
// Tools Library for the Protea Project / Run Length Encoding
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "RLE.h"
#include <iostream>

const unsigned char RLE_CODE = 254;

long RLECompress( unsigned char* source, long src_size, unsigned char* dest, long dest_size ) {
	unsigned char lastchar;
	unsigned char* itemp;
	unsigned char* otemp = dest;
	unsigned char* stop_point = source + src_size;
	unsigned char count = 0;
	long out_size = 0;

	// Get the first two characters
	lastchar = *source;
	itemp = source + 1;

	while (itemp < stop_point) {
		// Check for similar characters
		if (lastchar == *itemp) {
			// We have duplicate characters, count how many
			count = 2;
			itemp++;
			while (*itemp == lastchar && count < 254 && itemp < stop_point) {
				itemp++;
				count++;
			};

			// Only output compressed form if more than 3 characters
			if (count > 3 && out_size + 3 <= dest_size) {
				*otemp = RLE_CODE;
				*(otemp+1) = count;
				*(otemp+2) = lastchar;

				out_size += 3;
				otemp += 3;
			} else if (out_size + count <= dest_size) {
				memset( otemp, lastchar, count );
				out_size += count;
				otemp += count;
			} else
				return 0;
		} else {
			// Output the current character
			if (lastchar != RLE_CODE && out_size < dest_size) {
				out_size++;
				*otemp++ = lastchar;
			} else if (out_size + 3 <= dest_size){
				// Have to output RLE_CODE
				*otemp = RLE_CODE;
				*(otemp+1) = 1;
				*(otemp+2) = lastchar;

				otemp += 3;
				out_size += 3;
			} else
				return 0;
		};

		lastchar = *itemp;
		itemp++;
	};

	// Add the final character
	if (itemp == stop_point) {
		if (out_size < dest_size) {
			*otemp = lastchar;
			out_size++;
		} else
			return 0;
	};

	return out_size;
};






long RLEUncompress( unsigned char* source, long source_size, unsigned char* dest, long dest_size ) {
	unsigned char* stop_point = source + source_size;
	unsigned char* itemp = source;
	long dest_count = 0;

	while (itemp < stop_point) {
		if (*itemp != RLE_CODE) {
			if (dest_count < dest_size) {
				*dest++ = *itemp;
				dest_count++;
			} else
				return dest_count;
		} else {
			// Compressed stuff
			itemp++;
			unsigned char count = *itemp++;
			if (dest_size >= count + dest_count) {
				memset( dest, *itemp, count );
				dest += count;
				dest_count += count;
			} else
				return dest_count;
		};

		itemp++;
	};

	return dest_count;
};
